gdk-pixbuf/gdk-pixbuf-io.c gdk-pixbuf/gdk-pixbuf-private.h
authorSven Neumann <sven@gimp.org>
Fri, 5 Oct 2001 18:51:47 +0000 (18:51 +0000)
committerSven Neumann <neo@src.gnome.org>
Fri, 5 Oct 2001 18:51:47 +0000 (18:51 +0000)
2001-10-05  Sven Neumann  <sven@gimp.org>

* gdk-pixbuf/gdk-pixbuf-io.c
* gdk-pixbuf/gdk-pixbuf-private.h
* gdk-pixbuf/gdk-pixbuf.c
* gdk-pixbuf/gdk-pixbuf.h
* gdk-pixbuf/io-jpeg.c
* gdk-pixbuf/io-png.c: changed GDK_PIXBUF_ERROR_BAD_OPTION_VALUE to
GDK_PIXBUF_ERROR_BAD_OPTION to we can use it for bad keys too. Added
new public API gdk_pixbuf_get_option() to retrieve key/value pairs
set by an image loader. Added support for saving and reading PNG tEXt
chunks in PNG images.

* demos/testpixbuf-save.c
* demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk
feature.

* gdk-pixbuf/tmpl/gdk-pixbuf.sgml: adapt to changes in GDK_PIXBUF_ERROR
enum.

18 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
demos/testpixbuf-save.c
demos/testpixbuf-scale.c
docs/reference/ChangeLog
docs/reference/gdk-pixbuf/tmpl/gdk-pixbuf.sgml
gdk-pixbuf/ChangeLog
gdk-pixbuf/gdk-pixbuf-io.c
gdk-pixbuf/gdk-pixbuf-private.h
gdk-pixbuf/gdk-pixbuf.c
gdk-pixbuf/gdk-pixbuf.h
gdk-pixbuf/io-jpeg.c
gdk-pixbuf/io-png.c

index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 0cd97094b2e8d1a224ef6218db75c4bac4564330..604368417d5fa4e0ae4e404e741667ab4f4fad3d 100644 (file)
@@ -1,3 +1,9 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * demos/testpixbuf-save.c
+       * demos/testpixbuf-scale.c: simple tests for the new PNG tEXt chunk 
+       feature.
+
 Fri Oct  5 19:06:07 2001  Kristian Rietveld  <kristian@planet.nl>
 
        * gtk/gtktreeview.c (gtk_tree_view_leave_notify): should
index 6c6a1da70deb6af4acc2b55220dd670ff88c1505..88cac525ddd3376f197c01cd0377661cb240c5ea 100644 (file)
@@ -35,7 +35,10 @@ keypress_check (GtkWidget *widget, GdkEventKey *evt, gpointer data)
                         return;
                 }
 
-                if (!gdk_pixbuf_save (pixbuf, "foo.png", "png", &err, NULL)) {
+                if (!gdk_pixbuf_save (pixbuf, "foo.png", "png", 
+                                      &err,
+                                      "tEXt::Software", "testpixbuf-save",
+                                      NULL)) {
                         fprintf (stderr, "%s", err->message);
                         g_error_free (err);
                 }
index fd6c801dca01f71e743e640a15e8373b1b22759d..959fa115c90bff6a1710c4ec9fdd0bba1161da3b 100644 (file)
@@ -63,6 +63,7 @@ main(int argc, char **argv)
        GtkWidget *hbox, *label, *hscale;
        GtkAdjustment *adjustment;
        GtkRequisition scratch_requisition;
+        const gchar *creator;
         GError *error;
         
        pixbuf_init ();
@@ -83,6 +84,10 @@ main(int argc, char **argv)
                exit(1);
        }
 
+        creator = gdk_pixbuf_get_option (pixbuf, "tEXt::Software");
+        if (creator)
+                g_print ("%s was created by '%s'\n", argv[1], creator);
+
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_signal_connect (GTK_OBJECT (window), "destroy",
                            GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
index 416106d08ff13c5acd33e0e8204c8ac7e99b864f..8c195b7f481e69a279d6cd0065b23a9e767dc024 100644 (file)
@@ -1,3 +1,8 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * gdk-pixbuf/tmpl/gdk-pixbuf.sgml: adapt to changes in GDK_PIXBUF_ERROR
+       enum.
+
 2001-10-04  Havoc Pennington  <hp@pobox.com>
 
        * gtk/tmpl/gtkimage.sgml: fix GtkImage overview
index d7d5532f2fac54c6cf3898e67265d643f54c5a39..db969d54563ae38d9016a804bc17b33a32a8101e 100644 (file)
@@ -27,8 +27,7 @@ domain.
 @GDK_PIXBUF_ERROR_UNKNOWN_FORMAT: 
 @GDK_PIXBUF_ERROR_CORRUPT_IMAGE: An image file was broken somehow.
 @GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY: Not enough memory.
-@GDK_PIXBUF_ERROR_BAD_OPTION_VALUE: An option passed to
-gdk_pixbuf_save() had a bad value.
+@GDK_PIXBUF_ERROR_BAD_OPTION: A bad option was passed to a pixbuf save module.
 @GDK_PIXBUF_ERROR_UNKNOWN_TYPE: Unknown image type.
 @GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION: Don't know how to perform the
 given operation on the type of image at hand.
index bb93b87d8c3a3b2ea728b0635ea9973efd238fea..5f40de330042326ea06081d64c8a34f83bcd1320 100644 (file)
@@ -1,3 +1,16 @@
+2001-10-05  Sven Neumann  <sven@gimp.org>
+
+       * gdk-pixbuf/gdk-pixbuf-io.c
+       * gdk-pixbuf/gdk-pixbuf-private.h
+       * gdk-pixbuf/gdk-pixbuf.c
+       * gdk-pixbuf/gdk-pixbuf.h
+       * gdk-pixbuf/io-jpeg.c
+       * gdk-pixbuf/io-png.c: changed GDK_PIXBUF_ERROR_BAD_OPTION_VALUE to
+       GDK_PIXBUF_ERROR_BAD_OPTION to we can use it for bad keys too. Added
+       new public API gdk_pixbuf_get_option() to retrieve key/value pairs
+       set by an image loader. Added support for saving and reading PNG tEXt 
+       chunks in PNG images.
+
 Tue Oct  2 11:29:50 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gdk-pixdata.c (gdk_pixdata_to_csource): Fix indentation
index 5558ef65746cb3980334b9e79ebeba3323e0f513..e97036ac1c5ae4d2ba3f7785c333df74b8a8429c 100644 (file)
@@ -731,8 +731,8 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
  * @Varargs: list of key-value save options
  *
  * Saves pixbuf to a file in @type, which is currently "jpeg" or
- * "png".  If @error is set, FALSE will be returned. Possible errors include those
- * in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
+ * "png".  If @error is set, FALSE will be returned. Possible errors include 
+ * those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
  *
  * The variable argument list should be NULL-terminated; if not empty,
  * it should contain pairs of strings that modify the save
@@ -743,8 +743,13 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
  *                  "quality", "100", NULL);
  * </programlisting>
  *
- * The only save parameter that currently exists is the "quality" field
- * for JPEG images; its value should be in the range [0,100].
+ * Currently only few parameters exist. JPEG images can be saved with a 
+ * "quality" parameter; its value should be in the range [0,100]. 
+ * Text chunks can be attached to PNG images by specifying parameters of
+ * the form "tEXt::key", where key is an ASCII string of length 1-79.
+ * The values are UTF-8 encoded strings. Note however that PNG text
+ * chunks are stored in ISO-8859-1 encoding, so you can only set texts
+ * that can be represented in this encoding.
  *
  * Return value: whether an error was set
  **/
index 4393cbe336f977e6e85b38201f14ae2ff0612688..db13af8058e45f7e2a76750c76bb4745803e29b0 100644 (file)
@@ -128,8 +128,16 @@ struct _GdkPixbufAnimationIterClass {
                                         const GTimeVal         *current_time);
 };
       
-\f
 
 GdkPixbufAnimation* gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf);
 
+\f
+
+/*  key/value pairs that can be attached by the pixbuf loader  */
+
+gboolean gdk_pixbuf_set_option  (GdkPixbuf   *pixbuf,
+                                 const gchar *key,
+                                 const gchar *value);
+
+
 #endif
index 60750f4a05a6f2a01fc64709f8b35bc7e1b8295f..17df45fc7fd1dc67bef84d8c431e8e412e0e885d 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
 /* GdkPixbuf library - Basic memory management
  *
  * Copyright (C) 1999 The Free Software Foundation
@@ -479,6 +480,93 @@ gdk_pixbuf_fill (GdkPixbuf *pixbuf,
         }
 }
 
+\f
+
+/**
+ * gdk_pixbuf_get_option:
+ * @pixbuf: a #GdkPixbuf
+ * @key: a nul-terminated string.
+ * 
+ * Looks up @key in the list of options that may have been attached to the
+ * @pixbuf when it was loaded. 
+ * 
+ * Return value: the value associated with @key. This is a nul-terminated 
+ * string that should not be freed or %NULL if @key was not found.
+ **/
+G_CONST_RETURN gchar *
+gdk_pixbuf_get_option (GdkPixbuf   *pixbuf,
+                       const gchar *key)
+{
+        gchar **options;
+        gint i;
+
+        g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+        g_return_val_if_fail (key != NULL, NULL);
+  
+        options = g_object_get_qdata (G_OBJECT (pixbuf), 
+                                      g_quark_from_static_string ("gdk_pixbuf_options"));
+        if (options) {
+                for (i = 0; options[2*i]; i++) {
+                        if (strcmp (options[2*i], key) == 0)
+                                return options[2*i+1];
+                }
+        }
+        
+        return NULL;
+}
+
+/**
+ * gdk_pixbuf_set_option:
+ * @pixbuf: a #GdkPixbuf
+ * @key: a nul-terminated string.
+ * @value: a nul-terminated string.
+ * 
+ * Attaches a key/value pair as an option to a #GdkPixbuf. If %key already
+ * exists in the list of options attached to @pixbuf, the new value is 
+ * ignored and %FALSE is returned.
+ *
+ * Return value: %TRUE on success.
+ **/
+gboolean
+gdk_pixbuf_set_option (GdkPixbuf   *pixbuf,
+                       const gchar *key,
+                       const gchar *value)
+{
+        GQuark  quark;
+        gchar **options;
+        gint n = 0;
+        g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+        g_return_val_if_fail (key != NULL, FALSE);
+        g_return_val_if_fail (value != NULL, FALSE);
+
+        quark = g_quark_from_static_string ("gdk_pixbuf_options");
+
+        options = g_object_get_qdata (G_OBJECT (pixbuf), quark);
+
+        if (options) {
+                for (n = 0; options[2*n]; n++) {
+                        if (strcmp (options[2*n], key) == 0)
+                                return FALSE;
+                }
+
+                g_object_steal_qdata (G_OBJECT (pixbuf), quark);
+                options = g_renew (gchar *, options, 2*(n+1) + 1);
+        } else {
+                options = g_new (gchar *, 3);
+        }
+        
+        options[2*n]   = g_strdup (key);
+        options[2*n+1] = g_strdup (value);
+        options[2*n+2] = NULL;
+
+        g_object_set_qdata_full (G_OBJECT (pixbuf), quark,
+                                 options, (GDestroyNotify) g_strfreev);
+        
+        return TRUE;
+}
+
+\f
 
 /* Include the marshallers */
 #include <glib-object.h>
index 7d274d9d7282ff56715ce944b124c5e3da00f458..d0263bdc2d0ddb36951416258475cb0a313b9b96 100644 (file)
@@ -85,8 +85,8 @@ typedef enum {
         GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
         /* no mem to load image */
         GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-        /* bad option value passed to save routine */
-        GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
+        /* bad option passed to save routine */
+        GDK_PIXBUF_ERROR_BAD_OPTION,
         /* unsupported image type (sort of an ENOSYS) */
         GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
         /* unsupported operation (load, save) for image type */
@@ -286,6 +286,12 @@ gboolean                gdk_pixbuf_animation_iter_advance                    (Gd
                                                                               const GTimeVal         *current_time);
 
 
+\f
+
+G_CONST_RETURN gchar * gdk_pixbuf_get_option (GdkPixbuf   *pixbuf,
+                                              const gchar *key);
+
+\f
  
 #include <gdk-pixbuf/gdk-pixbuf-loader.h>
 #include <gdk-pixbuf/gdk-pixbuf-enum-types.h>
index f04c954f9eb322d541f002d3c054d9b3fd8c6f4a..ec054dd2cc032dfbf8936e705e016840bbbad926 100644 (file)
@@ -660,7 +660,7 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
                                if (endptr == *viter) {
                                        g_set_error (error,
                                                     GDK_PIXBUF_ERROR,
-                                                    GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
+                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
                                                     _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
                                                     *viter);
 
@@ -675,7 +675,7 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
                                         */
                                        g_set_error (error,
                                                     GDK_PIXBUF_ERROR,
-                                                    GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
+                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
                                                     _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
                                                     quality);
 
index f434e699d40bcf0a66fc9847eb484a365caca992..b531a75d28f7e5c0ad085f2de807e1029b1d96fb 100644 (file)
@@ -182,17 +182,39 @@ free_buffer (guchar *pixels, gpointer data)
        g_free (pixels);
 }
 
+static gboolean
+png_text_to_pixbuf_option (png_text   text_ptr,
+                           gchar    **key,
+                           gchar    **value)
+{
+        *value = g_convert (text_ptr.text, -1, 
+                            "UTF-8", "ISO-8859-1", 
+                            NULL, NULL, NULL);
+        if (*value) {
+                *key = g_strconcat ("tEXt::", text_ptr.key, NULL);
+                return TRUE;
+        } else {
+                g_warning ("Couldn't convert tEXt chunk value to UTF-8.");
+                *key = NULL;
+                return FALSE;
+        }
+}
+
 /* Shared library entry point */
 static GdkPixbuf *
 gdk_pixbuf__png_image_load (FILE *f, GError **error)
 {
+        GdkPixbuf *pixbuf;
        png_structp png_ptr;
        png_infop info_ptr, end_info;
+        png_textp text_ptr;
         gboolean failed = FALSE;
        gint i, ctype, bpp;
        png_uint_32 w, h;
        png_bytepp rows;
        guchar *pixels;
+        gint    num_texts;
+        gchar **options = NULL;
 
        png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
                                           error,
@@ -255,17 +277,42 @@ gdk_pixbuf__png_image_load (FILE *f, GError **error)
                rows[i] = pixels + i * w * bpp;
 
        png_read_image (png_ptr, rows);
+
+        if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
+                options = g_new (gchar *, num_texts * 2);
+                for (i = 0; i < num_texts; i++) {
+                        png_text_to_pixbuf_option (text_ptr[i], 
+                                                   options + 2*i, 
+                                                   options + 2*i + 1);
+                }
+        }
        png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
        g_free (rows);
 
        if (ctype & PNG_COLOR_MASK_ALPHA)
-               return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
-                                                w, h, w * 4,
-                                                free_buffer, NULL);
+               pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
+                                                   w, h, w * 4,
+                                                   free_buffer, NULL);
        else
-               return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
-                                                w, h, w * 3,
-                                                free_buffer, NULL);
+               pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
+                                                   w, h, w * 3,
+                                                   free_buffer, NULL);
+
+        if (options) {
+                for (i = 0; i < num_texts; i++) {
+                        if (pixbuf) {
+                                if (!gdk_pixbuf_set_option (pixbuf, 
+                                                            options[2*i], 
+                                                            options[2*i+1]))
+                                        g_warning ("Got multiple tEXt chunks for the same key.");
+                        }
+                        g_free (options[2*i]);
+                        g_free (options[2*i+1]);
+                }
+                g_free (options);
+        }
+
+        return pixbuf;
 }
 
 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
@@ -501,6 +548,8 @@ png_info_callback   (png_structp png_read_ptr,
 {
         LoadContext* lc;
         png_uint_32 width, height;
+        png_textp png_text_ptr;
+        int i, num_texts;
         int color_type;
         gboolean have_alpha = FALSE;
         gboolean failed = FALSE;
@@ -538,7 +587,22 @@ png_info_callback   (png_structp png_read_ptr,
                 }
                 return;
         }
+
+        /* Extract tEXt chunks and attach them as pixbuf options */
         
+        if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
+                for (i = 0; i < num_texts; i++) {
+                        gchar *key, *value;
+
+                        if (png_text_to_pixbuf_option (png_text_ptr[i],
+                                                       &key, &value)) {
+                                gdk_pixbuf_set_option (lc->pixbuf, key, value);
+                                g_free (key);
+                                g_free (value);
+                        }
+                }
+        }
+
         /* Notify the client that we are ready to go */
 
         if (lc->prepare_func)
@@ -653,32 +717,76 @@ gdk_pixbuf__png_image_save (FILE          *f,
 {
        png_structp png_ptr;
        png_infop info_ptr;
+       png_textp text_ptr = NULL;
        guchar *ptr;
        guchar *pixels;
-       int x, y, j;
+       int x, y;
+       int i, j;
        png_bytep row_ptr;
        png_bytep data;
        png_color_8 sig_bit;
        int w, h, rowstride;
        int has_alpha;
        int bpc;
+       int num_keys;
+
+       num_keys = 0;
 
        if (keys && *keys) {
-               g_warning ("Bad option name '%s' passed to PNG saver",
-                          *keys);
-               return FALSE;
-#if 0
-               gchar **kiter = keys;
-               gchar **viter = values;
-
-               
-               while (*kiter) {
-                       
-                       ++kiter;
-                       ++viter;
+               gchar **kiter;
+               gchar  *key;
+               int     len;
+
+               for (kiter = keys; *kiter; kiter++) {
+                       if (strncmp (*kiter, "tEXt::", 6) != 0) {
+                                g_warning ("Bad option name '%s' passed to PNG saver", *kiter);
+                                return FALSE;
+                       }
+                       key = *kiter + 6;
+                       len = strlen (key);
+                       if (len <= 1 || len > 79) {
+                               g_set_error (error,
+                                            GDK_PIXBUF_ERROR,
+                                            GDK_PIXBUF_ERROR_BAD_OPTION,
+                                            _("Keys for PNG tEXt chunks must have at least 1 and at most 79 characters."));
+                               return FALSE;
+                       }
+                       for (i = 0; i < len; i++) {
+                               if ((guchar) key[i] > 127) {
+                                       g_set_error (error,
+                                                    GDK_PIXBUF_ERROR,
+                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
+                                                    _("Keys for PNG tEXt chunks must be ASCII characters."));
+                                       return FALSE;
+                               }
+                       }
+                       num_keys++;
+               }
+       }
+
+       if (num_keys > 0) {
+               text_ptr = g_new0 (png_text, num_keys);
+               for (i = 0; i < num_keys; i++) {
+                       text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
+                       text_ptr[i].key  = keys[i] + 6;
+                       text_ptr[i].text = g_convert (values[i], -1, 
+                                                     "ISO-8859-1", "UTF-8", 
+                                                     NULL, &text_ptr[i].text_length, 
+                                                     NULL);
+                       if (!text_ptr[i].text) {
+                               g_set_error (error,
+                                            GDK_PIXBUF_ERROR,
+                                            GDK_PIXBUF_ERROR_BAD_OPTION,
+                                            _("Value for PNG tEXt chunk can not be converted to ISO-8859-1 encoding."));
+                               num_keys = i;
+                               for (i = 0; i < num_keys; i++)
+                                       g_free (text_ptr[i].text);
+                               g_free (text_ptr);
+                               return FALSE;
+                       }
                }
-#endif
        }
+
        data = NULL;
        
        bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
@@ -704,7 +812,13 @@ gdk_pixbuf__png_image_save (FILE          *f,
                png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
                return FALSE;
        }
+
+       if (num_keys > 0) {
+               png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
+       }
+
        png_init_io (png_ptr, f);
+
        if (has_alpha) {
                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
                              PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
@@ -763,6 +877,12 @@ gdk_pixbuf__png_image_save (FILE          *f,
        png_write_end (png_ptr, info_ptr);
        png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
 
+       if (num_keys > 0) {
+               for (i = 0; i < num_keys; i++)
+                       g_free (text_ptr[i].text);
+               g_free (text_ptr);
+       }
+
        return TRUE;
 }